/* PROJECT:         Lite sndrec32
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            base/applications/sndrec32/audio_membuffer.cpp
 * PURPOSE:         Sound recording
 * PROGRAMMERS:     Marco Pagliaricci (irc: rendar)
 */



#include "stdafx.h"
#include "audio_membuffer.hpp"



_AUDIO_NAMESPACE_START_




//////////////////////////////////////
/////// Protected Functions  /////////
//////////////////////////////////////


void 
audio_membuffer::alloc_mem_( unsigned int bytes )
{

    //
    // Some checking
    //

    if ( bytes == 0 )
        return;





    //
    // Checks previsiously alloc'd memory
    // and frees it.
    //

    if ( audio_data )
        delete[] audio_data;



    //
    // Allocs new memory and zeros it.
    //

    audio_data = new BYTE[ bytes ];


    memset( audio_data, 0, bytes * sizeof( BYTE ));



    //
    // Sets the correct buffer size
    //

    buf_size = bytes;


    init_size = bytes;



}


void
audio_membuffer::free_mem_( void )
{

    if ( audio_data )
        delete[] audio_data;

    buf_size = 0;
    audio_data = 0;

}


void
audio_membuffer::resize_mem_( unsigned int new_size )
{


    if ( new_size == 0 )
        return;


    //
    // The new_size, cannot be <= of the
    // `bytes_received' member value of the
    // parent class `audio_receiver'.
    // We cannot touch received audio data,
    // so we have to alloc at least
    // bytes_received+1 bytes.
    //
    // But we can truncate unused memory, so
    // `new_size' can be < of `buf_size'.
    //

    if ( new_size <= bytes_received )
        return;


    

    BYTE * new_mem;



    //
    // Allocs new memory and zeros it.
    //


    new_mem = new BYTE[ new_size ];

    memset( new_mem, 0, new_size * sizeof( BYTE ));



    if ( audio_data )
    {


        //
        // Copies received audio data, and discard
        // unused memory.
        //

        memcpy( new_mem, audio_data, bytes_received );



        //
        // Frees old memory.
        //

        delete[] audio_data;





        //
        // Commit new memory.
        //

        audio_data = new_mem;
        buf_size = new_size;




    } else {

        audio_data = new_mem;
        buf_size = new_size;
    }


    if ( buffer_resized )
        buffer_resized( new_size );

}




void 
audio_membuffer::truncate_( void )
{

    //
    // If `buf_size' is already = to the
    // `bytes_received' of audio data, then
    // this operation is useless; simply return.
    //

    if ( bytes_received == buf_size )
        return;



    if ( audio_data )
    {

        
        //
        // Allocs a new buffer.
        //

        BYTE * newbuf = new BYTE[ bytes_received ];




        //
        // Copies audio data.
        //

        memcpy( newbuf, audio_data, bytes_received );



        //
        // Frees old memory.
        //

        delete[] audio_data;



        //
        // Commit the new buffer.
        //

        audio_data = newbuf;
        buf_size = bytes_received;



        //
        // Buffer truncation successfull.
        // Now the buffer size is exactly big
        // as much audio data was received.
        //


    }


}






//////////////////////////////////////
/////// Public Functions   ///////////
//////////////////////////////////////




void
audio_membuffer::clear( void )
{

    free_mem_();

    bytes_received = 0;
}



void 
audio_membuffer::reset( void )
{


    //
    // Frees memory and reset
    // to initial state.
    //

    clear();



    //
    // Alloc memory of size specified
    // at the constructor.
    //

    alloc_mem_( init_size );


}

void 
audio_membuffer::alloc_bytes( unsigned int bytes )
{

    alloc_mem_( bytes );

}



        
void 
audio_membuffer::alloc_seconds( unsigned int secs )
{
    
    alloc_mem_( aud_info.byte_rate() * secs );

}


void 
audio_membuffer::alloc_seconds( float secs )
{

    alloc_mem_(( unsigned int )(( float ) aud_info.byte_rate() * secs ));

}




void 
audio_membuffer::resize_bytes( unsigned int bytes )
{

    resize_mem_( bytes );

}


        
void 
audio_membuffer::resize_seconds( unsigned int secs )
{

    resize_mem_( aud_info.byte_rate() * secs );

}


void 
audio_membuffer::resize_seconds( float secs )
{

    resize_mem_(( unsigned int )
        (( float )aud_info.byte_rate() * secs )
    );

}





///////////////////////////////////////
///////  Inherited Functions  /////////
///////////////////////////////////////







void 
audio_membuffer::audio_receive
        ( unsigned char * data, unsigned int size )
{

    


    //
    // If there isn't a buffer, allocs memory for
    // it of size*2, and copies audio data arrival.
    //

    if (( audio_data == 0 ) || ( buf_size == 0 ))
    {
        alloc_mem_( size * 2 );

        memcpy( audio_data, data, size );

        return;

    }





    //
    // If buffer's free memory is < of `size',
    // we have to realloc buffer memory of
    // buf_size*2, while free memory is enough
    // to contain `size' bytes.
    //
    // In this case free memory is represented
    // by `buf_size - bytes_recorded'.
    //

    unsigned int tot_mem = buf_size,
        free_mem = buf_size - bytes_received;


    if ( free_mem < size )
    {

        //
        // Calcs new buffer size.
        // TODO: flags for other behaviour?

        while ( free_mem < size )
        {
            tot_mem *= 2;

            free_mem = tot_mem - bytes_received;
        }



        //
        // Resize buffer memory.
        //

        resize_mem_( tot_mem );

    }
        

    //
    // Now we have enough free space in the
    // buffer, so let's copy audio data arrivals.
    //

    memcpy( audio_data + bytes_received, data, size );


    
    
    if ( audio_arrival )
        audio_arrival( aud_info.samples_in_bytes( size ));
    


}


unsigned int 
audio_membuffer::read( BYTE * out_buf, unsigned int bytes )
{


    //
    // Some checking
    //
    
    if ( !audio_data )
        return 0;


    if ( bytes_played_ >= bytes_received )
        return 0;



    unsigned int to_play =  
        bytes_received - bytes_played_;


    unsigned int to_copy = 
        bytes > to_play ? to_play : bytes;
    

    //
    // Copies the audio data out.
    //

    if (( out_buf ) && ( to_copy ) && ( audio_data ))
        memcpy( out_buf, audio_data + bytes_played_, to_copy );


    //
    // Increments the number of total bytes
    // played (audio data gone out from the
    // `audio_producer' object).
    //

    bytes_played_ += to_copy;


    if ( audio_arrival )
        audio_arrival( aud_info.samples_in_bytes( to_copy ));

    
    //
    // Returns the exact size of audio data
    // produced.
    //

    return to_copy;
}


bool
audio_membuffer::finished( void ) 
{
    if ( bytes_played_ < bytes_received )
        return false;
    else
        return true;
}


_AUDIO_NAMESPACE_END_
